home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Very Best of Atari Inside
/
The Very Best of Atari Inside 1.iso
/
mint
/
mint110s
/
unifs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-11
|
19KB
|
808 lines
/*
Copyright 1991,1992 Eric R. Smith.
Copyright 1992,1993,1994 Atari Corporation.
All rights reserved.
*/
/* a simple unified file system */
#include "mint.h"
extern FILESYS bios_filesys, proc_filesys, pipe_filesys, shm_filesys;
static long ARGS_ON_STACK uni_root P_((int drv, fcookie *fc));
static long ARGS_ON_STACK uni_lookup P_((fcookie *dir, const char *name, fcookie *fc));
static long ARGS_ON_STACK uni_getxattr P_((fcookie *fc, XATTR *xattr));
static long ARGS_ON_STACK uni_chattr P_((fcookie *fc, int attrib));
static long ARGS_ON_STACK uni_chown P_((fcookie *fc, int uid, int gid));
static long ARGS_ON_STACK uni_chmode P_((fcookie *fc, unsigned mode));
static long ARGS_ON_STACK uni_rmdir P_((fcookie *dir, const char *name));
static long ARGS_ON_STACK uni_remove P_((fcookie *dir, const char *name));
static long ARGS_ON_STACK uni_getname P_((fcookie *root, fcookie *dir,
char *pathname, int size));
static long ARGS_ON_STACK uni_rename P_((fcookie *olddir, char *oldname,
fcookie *newdir, const char *newname));
static long ARGS_ON_STACK uni_opendir P_((DIR *dirh, int flags));
static long ARGS_ON_STACK uni_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *));
static long ARGS_ON_STACK uni_rewinddir P_((DIR *dirh));
static long ARGS_ON_STACK uni_closedir P_((DIR *dirh));
static long ARGS_ON_STACK uni_pathconf P_((fcookie *dir, int which));
static long ARGS_ON_STACK uni_dfree P_((fcookie *dir, long *buf));
static DEVDRV * ARGS_ON_STACK uni_getdev P_((fcookie *fc, long *devsp));
static long ARGS_ON_STACK uni_symlink P_((fcookie *dir, const char *name, const char *to));
static long ARGS_ON_STACK uni_readlink P_((fcookie *fc, char *buf, int buflen));
static long ARGS_ON_STACK uni_fscntl P_((fcookie *dir, const char *name, int cmd, long arg));
FILESYS uni_filesys = {
(FILESYS *)0,
FS_LONGPATH,
uni_root,
uni_lookup, nocreat, uni_getdev, uni_getxattr,
uni_chattr, uni_chown, uni_chmode,
nomkdir, uni_rmdir, uni_remove, uni_getname, uni_rename,
uni_opendir, uni_readdir, uni_rewinddir, uni_closedir,
uni_pathconf, uni_dfree, nowritelabel, noreadlabel,
uni_symlink, uni_readlink, nohardlink, uni_fscntl, nodskchng
};
/*
* structure that holds files
* if (mode & S_IFMT == S_IFDIR), then this is an alias for a drive:
* "dev" holds the appropriate BIOS device number, and
* "data" is meaningless
* if (mode & S_IFMT == S_IFLNK), then this is a symbolic link:
* "dev" holds the user id of the owner, and
* "data" points to the actual link data
*/
typedef struct unifile {
char name[NAME_MAX+1];
ushort mode;
ushort dev;
FILESYS *fs;
void *data;
struct unifile *next;
} UNIFILE;
static UNIFILE u_drvs[UNI_NUM_DRVS];
static UNIFILE *u_root = 0;
static long do_ulookup P_((fcookie *, const char *, fcookie *, UNIFILE **));
FILESYS *
get_filesys (dev)
int dev;
{
UNIFILE *u;
for (u = u_root; u; u = u->next)
if (u->dev == dev)
return u->fs;
return (FILESYS *) 0L;
}
void
unifs_init()
{
UNIFILE *u = u_drvs;
int i;
u_root = u;
for (i = 0; i < UNI_NUM_DRVS; i++,u++) {
u->next = u+1;
u->mode = S_IFDIR|DEFAULT_DIRMODE;
u->dev = i;
if (i == PROCDRV) {
strcpy(u->name, "proc");
u->fs = &proc_filesys;
} else if (i == PIPEDRV) {
strcpy(u->name, "pipe");
u->fs = &pipe_filesys;
} else if (i == BIOSDRV) {
strcpy(u->name, "dev");
u->fs = &bios_filesys;
} else if (i == UNIDRV) {
(u-1)->next = u->next; /* skip this drive */
} else if (i == SHMDRV) {
strcpy(u->name, "shm");
u->fs = &shm_filesys;
} else {
u->name[0] = i + 'a';
u->name[1] = 0;
u->fs = 0;
}
}
--u; /* oops, we went too far */
u->next = 0;
}
static long ARGS_ON_STACK
uni_root(drv, fc)
int drv;
fcookie *fc;
{
if (drv == UNIDRV) {
fc->fs = &uni_filesys;
fc->dev = drv;
fc->index = 0L;
return 0;
}
fc->fs = 0;
return EINTRN;
}
static long ARGS_ON_STACK
uni_lookup(dir, name, fc)
fcookie *dir;
const char *name;
fcookie *fc;
{
return do_ulookup(dir, name, fc, (UNIFILE **)0);
}
/* worker function for uni_lookup; can also return the UNIFILE
* pointer for the root directory
*/
static long
do_ulookup(dir, name, fc, up)
fcookie *dir;
const char *name;
fcookie *fc;
UNIFILE **up;
{
UNIFILE *u;
long drvs;
FILESYS *fs;
fcookie *tmp;
extern long dosdrvs;
TRACE(("uni_lookup(%s)", name));
if (dir->index != 0) {
DEBUG(("uni_lookup: bad directory"));
return EPTHNF;
}
/* special case: an empty name in a directory means that directory */
/* so do "." and ".." */
if (!*name || !strcmp(name, ".") || !strcmp(name, "..")) {
dup_cookie(fc, dir);
return 0;
}
drvs = drvmap() | dosdrvs | PSEUDODRVS;
/*
* OK, check the list of aliases and special directories
*/
for (u = u_root; u; u = u->next) {
if (!stricmp(name, u->name)) {
if ( (u->mode & S_IFMT) == S_IFDIR ) {
if (u->dev >= NUM_DRIVES) {
fs = u->fs;
if (up) *up = u;
return (*fs->root)(u->dev,fc);
}
if ((drvs & (1L << u->dev)) == 0)
return EPTHNF;
tmp = &curproc->root[u->dev];
if (!tmp->fs) { /* drive changed? */
changedrv(tmp->dev);
tmp = &curproc->root[u->dev];
if (!tmp->fs)
return EPTHNF;
}
dup_cookie(fc, tmp);
} else { /* a symbolic link */
fc->fs = &uni_filesys;
fc->dev = UNIDRV;
fc->index = (long)u;
}
if (up) *up = u;
return 0;
}
}
DEBUG(("uni_lookup: name (%s) not found", name));
return EFILNF;
}
static long ARGS_ON_STACK
uni_getxattr(fc, xattr)
fcookie *fc;
XATTR *xattr;
{
UNIFILE *u = (UNIFILE *)fc->index;
if (fc->fs != &uni_filesys) {
ALERT("ERROR: wrong file system getxattr called");
return EINTRN;
}
xattr->index = fc->index;
xattr->dev = fc->dev;
xattr->nlink = 1;
xattr->blksize = 1;
/* If "u" is null, then we have the root directory, otherwise
* we use the UNIFILE structure to get the info about it
*/
if (!u || ( (u->mode & S_IFMT) == S_IFDIR )) {
xattr->uid = xattr->gid = 0;
xattr->size = xattr->nblocks = 0;
xattr->mode = S_IFDIR | DEFAULT_DIRMODE;
xattr->attr = FA_DIR;
} else {
xattr->uid = u->dev;
xattr->gid = 0;
xattr->size = xattr->nblocks = strlen(u->data) + 1;
xattr->mode = u->mode;
xattr->attr = 0;
}
xattr->mtime = xattr->atime = xattr->ctime = 0;
xattr->mdate = xattr->adate = xattr->cdate = 0;
return 0;
}
static long ARGS_ON_STACK
uni_chattr(dir, attrib)
fcookie *dir;
int attrib;
{
UNUSED(dir); UNUSED(attrib);
return EACCDN;
}
static long ARGS_ON_STACK
uni_chown(dir, uid, gid)
fcookie *dir;
int uid, gid;
{
UNUSED(dir); UNUSED(uid);
UNUSED(gid);
return EINVFN;
}
static long ARGS_ON_STACK
uni_chmode(dir, mode)
fcookie *dir;
unsigned mode;
{
UNUSED(dir);
UNUSED(mode);
return EINVFN;
}
static long ARGS_ON_STACK
uni_rmdir(dir, name)
fcookie *dir;
const char *name;
{
long r;
r = uni_remove(dir, name);
if (r == EFILNF) r = EPTHNF;
return r;
}
static long ARGS_ON_STACK
uni_remove(dir, name)
fcookie *dir;
const char *name;
{
UNIFILE *u, *lastu;
UNUSED(dir);
lastu = 0;
u = u_root;
while (u) {
if (!strncmp(u->name, name, NAME_MAX)) {
if ( (u->mode & S_IFMT) != S_IFLNK ) return EFILNF;
kfree(u->data);
if (lastu)
lastu->next = u->next;
else
u_root = u->next;
kfree(u);
return 0;
}
lastu = u;
u = u->next;
}
return EFILNF;
}
static long ARGS_ON_STACK
uni_getname(root, dir, pathname, size)
fcookie *root, *dir; char *pathname;
int size;
{
FILESYS *fs;
UNIFILE *u;
char *n;
fcookie relto;
char tmppath[PATH_MAX];
long r;
UNUSED(root);
if (size <= 0) return ERANGE;
fs = dir->fs;
if (dir->dev == UNIDRV) {
*pathname = 0;
return 0;
}
for (u = u_root; u; u = u->next) {
if (dir->dev == u->dev && (u->mode & S_IFMT) == S_IFDIR) {
*pathname++ = '\\';
if (--size <= 0) return ERANGE;
for (n = u->name; *n; ) {
*pathname++ = *n++;
if (--size <= 0) return ERANGE;
}
break;
}
}
if (!u) {
ALERT("unifs: couldn't match a drive with a directory");
return EPTHNF;
}
if (dir->dev >= NUM_DRIVES) {
if ((*fs->root)(dir->dev, &relto) == 0) {
if (!(fs->fsflags & FS_LONGPATH)) {
r = (*fs->getname)(&relto, dir, tmppath, PATH_MAX);
release_cookie(&relto);
if (r) {
return r;
}
if (strlen(tmppath) < size) {
strcpy(pathname, tmppath);
return 0;
} else {
return ERANGE;
}
}
r = (*fs->getname)(&relto, dir, pathname, size);
release_cookie(&relto);
return r;
} else {
*pathname = 0;
return EINTRN;
}
}
if (curproc->root[dir->dev].fs != fs) {
ALERT("unifs: drive's file system doesn't match directory's");
return EINTRN;
}
if (!(fs->fsflags & FS_LONGPATH)) {
r = (*fs->getname)(&curproc->root[dir->dev], dir, tmppath, PATH_MAX);
if (r) return r;
if (strlen(tmppath) < size) {
strcpy(pathname, tmppath);
return 0;
} else {
return ERANGE;
}
}
return (*fs->getname)(&curproc->root[dir->dev], dir, pathname, size);
}
static long ARGS_ON_STACK
uni_rename(olddir, oldname, newdir, newname)
fcookie *olddir;
char *oldname;
fcookie *newdir;
const char *newname;
{
UNIFILE *u;
fcookie fc;
long r;
UNUSED(olddir);
for (u = u_root; u; u = u->next) {
if (!stricmp(u->name, oldname))
break;
}
if (!u) {
DEBUG(("uni_rename: old file not found"));
return EFILNF;
}
/* the new name is not allowed to exist! */
r = uni_lookup(newdir, newname, &fc);
if (r == 0)
release_cookie(&fc);
if (r != EFILNF) {
DEBUG(("uni_rename: error %ld", r));
return (r == 0) ? EACCDN : r;
}
(void)strncpy(u->name, newname, NAME_MAX);
return 0;
}
static long ARGS_ON_STACK
uni_opendir(dirh, flags)
DIR *dirh;
int flags;
{
UNUSED(flags);
if (dirh->fc.index != 0) {
DEBUG(("uni_opendir: bad directory"));
return EPTHNF;
}
dirh->index = 0;
return 0;
}
static long ARGS_ON_STACK
uni_readdir(dirh, name, namelen, fc)
DIR *dirh;
char *name;
int namelen;
fcookie *fc;
{
long map;
char *dirname;
int i;
int giveindex = (dirh->flags == 0);
UNIFILE *u;
long index;
extern long dosdrvs;
long r;
map = dosdrvs | drvmap() | PSEUDODRVS;
i = dirh->index++;
u = u_root;
while (i > 0) {
--i;
u = u->next;
if (!u)
break;
}
tryagain:
if (!u) return ENMFIL;
dirname = u->name;
index = (long)u;
if ( (u->mode & S_IFMT) == S_IFDIR ) {
/* make sure the drive really exists */
if ( u->dev >= NUM_DRIVES) {
r = (*u->fs->root)(u->dev,fc);
if (r) {
fc->fs = &uni_filesys;
fc->index = 0;
fc->dev = u->dev;
}
} else {
if ((map & (1L << u->dev)) == 0 ) {
dirh->index++;
u = u->next;
goto tryagain;
}
dup_cookie(fc, &curproc->root[u->dev]);
if (!fc->fs) { /* drive not yet initialized */
/* use default attributes */
fc->fs = &uni_filesys;
fc->index = 0;
fc->dev = u->dev;
}
}
} else { /* a symbolic link */
fc->fs = &uni_filesys;
fc->dev = UNIDRV;
fc->index = (long)u;
}
if (giveindex) {
namelen -= (int)sizeof(long);
if (namelen <= 0) {
release_cookie(fc);
return ERANGE;
}
*((long *)name) = index;
name += sizeof(long);
}
strncpy(name, dirname, namelen-1);
if (strlen(name) < strlen(dirname)) {
release_cookie(fc);
return ENAMETOOLONG;
}
return 0;
}
static long ARGS_ON_STACK
uni_rewinddir(dirh)
DIR *dirh;
{
dirh->index = 0;
return 0;
}
static long ARGS_ON_STACK
uni_closedir(dirh)
DIR *dirh;
{
UNUSED(dirh);
return 0;
}
static long ARGS_ON_STACK
uni_pathconf(dir, which)
fcookie *dir;
int which;
{
UNUSED(dir);
switch(which) {
case -1:
return DP_MAXREQ;
case DP_IOPEN:
return 0; /* no files to open */
case DP_MAXLINKS:
return 1; /* no hard links available */
case DP_PATHMAX:
return PATH_MAX;
case DP_NAMEMAX:
return NAME_MAX;
case DP_ATOMIC:
return 1; /* no atomic writes */
case DP_TRUNC:
return DP_AUTOTRUNC;
case DP_CASE:
return DP_CASEINSENS;
default:
return EINVFN;
}
}
static long ARGS_ON_STACK
uni_dfree(dir, buf)
fcookie *dir;
long *buf;
{
UNUSED(dir);
buf[0] = 0; /* number of free clusters */
buf[1] = 0; /* total number of clusters */
buf[2] = 1; /* sector size (bytes) */
buf[3] = 1; /* cluster size (sectors) */
return 0;
}
static DEVDRV * ARGS_ON_STACK
uni_getdev(fc, devsp)
fcookie *fc;
long *devsp;
{
UNUSED(fc);
*devsp = EACCDN;
return 0;
}
static long ARGS_ON_STACK
uni_symlink(dir, name, to)
fcookie *dir;
const char *name;
const char *to;
{
UNIFILE *u;
fcookie fc;
long r;
r = uni_lookup(dir, name, &fc);
if (r == 0) {
release_cookie(&fc);
return EACCDN; /* file already exists */
}
if (r != EFILNF) return r; /* some other error */
u = kmalloc(SIZEOF(UNIFILE));
if (!u) return EACCDN;
strncpy(u->name, name, NAME_MAX);
u->name[NAME_MAX] = 0;
u->data = kmalloc((long)strlen(to)+1);
if (!u->data) {
kfree(u);
return EACCDN;
}
strcpy(u->data, to);
u->mode = S_IFLNK | DEFAULT_DIRMODE;
u->dev = curproc->ruid;
u->next = u_root;
u->fs = &uni_filesys;
u_root = u;
return 0;
}
static long ARGS_ON_STACK
uni_readlink(fc, buf, buflen)
fcookie *fc;
char *buf;
int buflen;
{
UNIFILE *u;
u = (UNIFILE *)fc->index;
assert(u);
assert((u->mode & S_IFMT) == S_IFLNK);
assert(u->data);
strncpy(buf, u->data, buflen);
if (strlen(u->data) >= buflen)
return ENAMETOOLONG;
return 0;
}
/* uk: use these Dcntl's to install a new filesystem which is only visible
* on drive u:
*
* FS_INSTALL: let the kernel know about the file system; it does NOT
* get a device number.
* FS_MOUNT: use Dcntl(FS_MOUNT, "u:\\foo", &descr) to make a directory
* foo where the filesytem resides in; the file system now
* gets its device number which is also written into the
* dev_no field of the fs_descr structure.
* FS_UNMOUNT: remove a file system's directory; this call closes all
* open files, directory searches and directories on this
* device. Make sure that the FS will not recognise any
* accesses to this device, as fs->root will be called
* during the reinitalisation!
* FS_UNINSTALL: remove a file system completely from the kernel list,
* but that will only be possible if there is no directory
* associated with this file system.
* This function allows it to write file systems as demons
* which stay in memory only as long as needed.
*
* BUG: it is not possible yet to lock such a filesystem.
*/
/* here we start with gemdos only file system device numbers */
static curr_dev_no = 0x100;
static long ARGS_ON_STACK
uni_fscntl(dir, name, cmd, arg)
fcookie *dir;
const char *name;
int cmd;
long arg;
{
fcookie fc;
long r;
extern struct kerinfo kernelinfo;
extern FILESYS *active_fs;
if (cmd == FS_INSTALL) { /* install a new filesystem */
struct fs_descr *d = (struct fs_descr*)arg;
FILESYS *fs;
/* check if FS is installed already */
for (fs = active_fs; fs; fs = fs->next)
if (d->file_system == fs) return 0L;
/* include new file system into chain of file systems */
d->file_system->next = active_fs;
active_fs = d->file_system;
return (long)&kernelinfo; /* return pointer to kernel info as OK */
} else if (cmd == FS_MOUNT) { /* install a new gemdos-only device for this FS */
struct fs_descr *d = (struct fs_descr*)arg;
FILESYS *fs;
UNIFILE *u;
/* first check for existing names */
r = uni_lookup(dir, name, &fc);
if (r == 0) {
release_cookie(&fc);
return EACCDN; /* name exists already */
}
if (r != EFILNF) return r; /* some other error */
if (!d) return EACCDN;
if (!d->file_system) return EACCDN;
/* check if FS is installed */
for (fs = active_fs; fs; fs = fs->next)
if (d->file_system == fs) break;
if (!fs) return EACCDN; /* not installed, so return an error */
u = kmalloc(SIZEOF(UNIFILE));
if (!u) return EACCDN;
strncpy(u->name, name, NAME_MAX);
u->name[NAME_MAX] = 0;
u->mode = S_IFDIR|DEFAULT_DIRMODE;
u->data = 0;
u->fs = d->file_system;
/* now get the file system its own device number */
u->dev = d->dev_no = curr_dev_no++;
/* chain new entry into unifile list */
u->next = u_root;
u_root = u;
return (long)u->dev;
} else if (cmd == FS_UNMOUNT) { /* remove a file system's directory */
struct fs_descr *d = (struct fs_descr*)arg;
FILESYS *fs;
UNIFILE *u;
/* first check that directory exists */
/* use special uni_lookup mode to get the unifile entry */
r = do_ulookup(dir, name, &fc, &u);
if (r != 0) return EFILNF; /* name does not exist */
if (!d) return EFILNF;
if (!d->file_system) return EFILNF;
if (d->file_system != fc.fs)
return EFILNF; /* not the right name! */
release_cookie(&fc);
if (!u || (u->fs != d->file_system))
return EFILNF;
/* check if FS is installed */
for (fs = active_fs; fs; fs = fs->next)
if (d->file_system == fs) break;
if (!fs) return EACCDN; /* not installed, so return an error */
/* here comes the difficult part: we have to close all files on that
* device, so we have to call changedrv(). The file system driver
* has to make sure that further calls to fs.root() with this device
* number will fail!
*
* Kludge: mark the directory as a link, so uni_remove will remove it.
*/
changedrv(u->dev);
u->mode &= ~S_IFMT;
u->mode |= S_IFLNK;
return uni_remove(dir, name);
} else if (cmd == FS_UNINSTALL) { /* remove file system from kernel list */
struct fs_descr *d = (struct fs_descr*)arg;
FILESYS *fs, *last_fs;
UNIFILE *u;
/* first check if there are any files or directories associated with
* this file system
*/
for (u = u_root; u; u = u->next)
if (u->fs == d->file_system)
return EACCDN; /* we cannot remove it before unmount */
last_fs = 0;
fs = active_fs;
while (fs) { /* go through the list and remove the file system */
if (fs == d->file_system) {
if (last_fs)
last_fs->next = fs->next;
else
active_fs = fs->next;
d->file_system->next = 0;
return 0;
}
last_fs = fs;
fs = fs->next;
}
return EFILNF;
} else {
/* see if we should just pass this along to another file system */
r = uni_lookup(dir, name, &fc);
if (r == 0) {
if (fc.fs != &uni_filesys) {
r = (*fc.fs->fscntl)(&fc, ".", cmd, arg);
release_cookie(&fc);
return r;
}
release_cookie(&fc);
}
}
return EINVFN;
}